package away3d.animators.skeleton ;


/**
 * Author: David Lenaerts
 */

import away3d.core.math.Quaternion ;
import flash.geom.Vector3D ;



class SkeletonNaryLERPNode extends SkeletonTreeNode
{
    
    
    /**
     * The weights for each joint. The total needs to equal 1.
     */
    private var _blendWeights   : Vector<Float> ;
    private var _inputs         : Vector<SkeletonTreeNode> ;
    private var _numInputs      : Int;
    
    
    
    public function new()
    {
        
        super();
        _inputs         = new Vector() ;
        _blendWeights   = new Vector() ;
        
    }
    
    
    public function getInputIndex( input: SkeletonTreeNode ) : Int
    {
        
        return _inputs.indexOf( input );
        
    }
    
    
    public function getInputAt( index: Int ) : SkeletonTreeNode
    {
        
        return _inputs[ index ] ;
        
    }
    
    
    public function addInput( input: SkeletonTreeNode)
    {
        //TODO: change access?
        _inputs[ _numInputs ]           = input ;
        _blendWeights[ _numInputs++ ]   = 0 ;
        
    }
    
    
    override public function updatePose( skeleton: Skeleton )
    {
        
        var input       : SkeletonTreeNode ;
        var weight      : Float ;
        var endPoses                            = skeletonPose.jointPoses;
        var poses       : Vector<JointPose>;
        var endPose     : JointPose
        var pose        : JointPose ;
        var endTr       : Vector3D ;
        var tr          : Vector3D ;
        var endQuat     : Quaternion ;
        var q           : Quaternion ;
        var firstPose   : Vector<JointPose> ;
        var i           : Int ;
        var w0          : Float ;
        var x0          : Float ;
        var y0          : Float ;
        var z0          : Float ;
        var w1          : Float ;
        var x1          : Float ;
        var y1          : Float ;
        var z1          : Float ;
        var numJoints                           = skeleton.numJoints ;
        
        // :s
        if( endPoses.length != numJoints ) endPoses.length = numJoints ;
        
        for( j in 0..._numInputs )
        {
            
            weight = _blendWeights[ j ] ;
            
            // TODO: Remove this continue!? with if?
            if( !weight == 0 ) //continue ;
            {
                input           = _inputs[ j ] ;
                input.time      = _time ;
                input.direction = _direction ;
                
                input.updatePose( skeleton ) ;
                
                poses           = input.skeletonPose.jointPoses;
                
                if( !firstPose )
                {
                    
                    firstPose = poses;
                    for( i in 0...numJoints )
                    {
                        endPose = endPoses[i] ||= new JointPose();
                        if( endPose === null ) endPose = new JointPose() ;// TODO: also set endPoses[ i ]?
                        
                        pose        = poses[ i ] ;
                        q           = pose.orientation ;
                        tr          = pose.translation ;
                        endQuat     = endPose.orientation ;
                        endQuat.x   = weight*q.x ;
                        endQuat.y   = weight*q.y ;
                        endQuat.z   = weight*q.z ;
                        endQuat.w   = weight*q.w ;
                        endTr       = endPose.translation ;
                        endTr.x     = weight*tr.x ;
                        endTr.y     = weight*tr.y ;
                        endTr.z     = weight*tr.z ;
                        
                    }
                    
                }
                
            }
            else
            {
                
                for( i in 0...skeleton.numJoints )
                {
                    
                    endPose = endPoses[ i ] ;
                    pose    = poses[ i ] ;
                    
                    q       = firstPose[ i ].orientation ;
                    x0      = q.x ;
                    y0      = q.y ;
                    z0      = q.z ;
                    w0      = q.w ;
                    
                    q       = pose.orientation ;
                    tr      = pose.translation ;
                    
                    x1      = q.x ;
                    y1      = q.y ;
                    z1      = q.z ;
                    w1      = q.w ;
                    
                    // find shortest direction
                    if( x0*x1 + y0*y1 + z0*z1 + w0*w1 < 0 )
                    {
                        
                        x1 = -x1 ;
                        y1 = -y1 ;
                        z1 = -z1 ;
                        w1 = -w1 ;
                        
                    }
                    
                    endQuat     = endPose.orientation ;
                    endQuat.x   += weight*x1 ;
                    endQuat.y   += weight*y1 ;
                    endQuat.z   += weight*z1 ;
                    endQuat.w   += weight*w1 ;
                    
                    endTr       = endPose.translation ;
                    endTr.x     += weight*tr.x ;
                    endTr.y     += weight*tr.y ;
                    endTr.z     += weight*tr.z ;
                    
                }
                
            }
            
        }
        
        for( i in 0...skeleton.numJoints )
        {
            
            endPoses[ i ].orientation.normalize() ;
            
        }
        
    }
    
    
    override public function updatePositionData() : void
    {
        
        var delta : Vector3D;
        var weight : Number;
        
        rootDelta.x = 0;
        rootDelta.y = 0;
        rootDelta.z = 0;
        
        for( j in 0..._numInputs )
        {
            
            weight = _blendWeights[ j ] ;
            
            if( !weight == 0 )
            {
                
                _inputs[ j ].time   = _time;
                _inputs[ j ].updatePositionData() ;
                 
                delta               = _inputs[ j ].rootDelta ;
                rootDelta.x         += weight*delta.x ;
                rootDelta.y         += weight*delta.y ;
                rootDelta.z         += weight*delta.z ;
                
            }
            
        }
        
    }
    
    
    public var blendWeights( get_blendWeights, null ) : Vector<Float> ;
    
    
    public function get_blendWeights() : Vector<Float>
    {
        
        return _blendWeights;
        
    }
    
    
    public function updateWeights( weights : Vector<Number> )
    {
        
        var weight : Float ;
        
        _duration       = 0 ;
        _blendWeights   = weights ;
        
        for( j in 0..._numInputs )
        {
            
            weight = _blendWeights[ j ] ;
            if( !weight == 0) _duration += weight*_inputs[ j ].duration ;
            
        }
        
    }
    
    
}
